﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Text;
using System.IO;
using System;

public class WrlExporter : MonoBehaviour {
    public  GameObject[] gameObjects;  //被导出模型的集合
    private struct Node                              //表示组合体结点的类型
    {
        public GameObject gObj;                //单个物体
        public int depth;                              //单个物体在组合体中的层级
    }
    private StringBuilder stringBuilder = new StringBuilder();   //携带模型信息的字符串
    public string pathName;                                           //路径名称

    /// Use this for initialization
    void Start()
    {
        ModelExportToWrl(gameObjects, pathName);
        print("Wrl模型在Assets\\Model(wrl)目录下，要在文件资源管理器查看");
    }

    /// <summary>
    /// 多个模型集合的Wrl导出，也是唯一的对外开放函数
    /// </summary>
    /// <param name="gameObjects">被导出模型的集合</param>
    /// <param name="pathName">路径名称</param>
    public void ModelExportToWrl(GameObject[] gameObjects,string pathName)
    {
        stringBuilder.Append("#VRML V2.0 utf8\r\n#Produced by DTS\r\n");
        for (int i=0;i<gameObjects.Length;i++)
        {
            CreateMeshWrl(gameObjects[i], pathName);
        }
        SaveTxtFile(stringBuilder.ToString(), @pathName);
    }

    /// <summary>
    /// 生成单个模型的wrl文件的函数
    /// </summary>
    /// <param name="gObj">单个被导出模型</param>
    /// <param name="pathName">路径名称</param>
    private  void CreateMeshWrl(GameObject gObj,string pathName)
    {
        List<Node> nodes = new List<Node>();    //模型结点列表
        List<bool> visitStatus = new List<bool>();  //判定结点是否访问过了
        int lastIndex;
        Node rootNode;
        rootNode.gObj = gObj;
        rootNode.depth =lastIndex= 0;
        nodes.Add(rootNode);
        visitStatus.Add(false);
        RecursiveAssembly(gObj,nodes,visitStatus);
        RecursiveModelInfo(stringBuilder,lastIndex,nodes,visitStatus);
    }

    /// <summary>
    /// 将字符串保存成文本文件
    /// </summary>
    /// <param name="text">文本内容</param>
    /// <param name="path">保存路径</param>
    private  void SaveTxtFile(string text, string path)
    {
        string str = Path.GetDirectoryName(path);
        if (!Directory.Exists(str))
        {
            Directory.CreateDirectory(str);
        }
        FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write);
        StreamWriter sr = new StreamWriter(fs);  
        sr.WriteLine(text);//开始写入值
        sr.Close();
        fs.Close();
    }

    /// <summary>
    /// 递归输出模型的信息，并安排好模型的层级关系
    /// </summary>
    /// <param name="stringBuilder">携带模型信息的字符串</param>
    /// <param name="lastIndex">模型结点列表中上一个结点的索引</param>
    /// <param name="nodes">模型结点列表</param>
    /// <param name="visitStatus">判定结点是否访问过了</param>
    private void RecursiveModelInfo( StringBuilder stringBuilder,int lastIndex,List<Node> nodes,List<bool> visitStatus)
    {
       for(int k=lastIndex;k<nodes.Count;k++)
            if (!visitStatus[k]) //判断结点是否访问过了
            {
                stringBuilder.Append("DEF " + nodes[k].gObj.transform.name + " Transform {\r\n");
                Vector3 parent = nodes[0].gObj.transform.position;
                Vector3 pos= nodes[k].gObj.transform.localPosition;
                stringBuilder.Append(string.Format("translation {0} {1} {2}\r\n", pos.x, pos.y, pos.z));
                Quaternion rot = nodes[k].gObj.transform.localRotation;
                float x=0, y=0, z=0, w=0,sinW=0;
                w = 2*Mathf.Acos(rot.w);
                sinW = Mathf.Sin(w*0.5f);
                if (sinW != 0)
                {
                    Vector3 rAxis = Vector3.zero;
                    rAxis.x = rot.x / Mathf.Sin(w);
                    rAxis.y = rot.y / Mathf.Sin(w);
                   rAxis.z = rot.z / Mathf.Sin(w);
                   rAxis= rAxis.normalized;
                    x = rAxis.x;  y = rAxis.y;  z = rAxis.z;
                    stringBuilder.Append(string.Format("rotation {0} {1} {2} {3}\r\n", x, y, z, w));
                }
                Vector3 sca = nodes[k].gObj.transform.localScale;
                if(sca!=new Vector3(1,1,1))
                    stringBuilder.Append(string.Format("scale {0} {1} {2}\r\n",sca.x,sca.y,sca.z));
                stringBuilder.Append("children [\r\n");
                stringBuilder.Append("Shape {\r\n");
                GenerateShapeInfo(stringBuilder, nodes[k],parent);
                stringBuilder.Append("}\r\n");  //shape
                if (k + 1 < nodes.Count)
                    //判定层级，下一个物体层级大的话就进入递归
                    if (nodes[k].depth< nodes[k + 1].depth)
                        RecursiveModelInfo(stringBuilder, k + 1,  nodes,  visitStatus);
                stringBuilder.Append("]\r\n");  //children
                stringBuilder.Append("}\r\n");  //transform
                visitStatus[k] = true;
                if (k + 1 < nodes.Count)
                    //判定层级，下一个物体层级小的话就跳出当前递归
                    if (nodes[k].depth > nodes[k + 1].depth)
                        return;  
            } 
    }

    /// <summary>
    /// 递归获取子模型，初始化结点列表
    /// </summary>
    /// <param name="go">被导出物体</param>
    /// <param name="nodes">模型结点列表</param>
    /// <param name="visitStatus">判定结点是否访问过了</param>
    private void RecursiveAssembly(GameObject go, List<Node> nodes, List<bool> visitStatus)
    {
        foreach(Transform child in go.transform )
        {
            int depth = 0;
            Transform temp = child;
            Node tempNode;
            while (temp.parent != null)
            {
                temp = temp.parent;
                depth++;
            }
            print(child.transform.name+"  "+ depth);
            tempNode.gObj = child.gameObject;
            tempNode.depth = depth;
            nodes.Add(tempNode);
            visitStatus.Add(false);
            RecursiveAssembly(child.gameObject,nodes,visitStatus);
        } 
    }

    /// <summary>
    /// 生成单个模型的“shape” 信息
    /// </summary>
    /// <param name="stringBuilder">携带模型信息的字符串</param>
    /// <param name="node">单个模型结点</param>
    private void  GenerateShapeInfo(StringBuilder stringBuilder,Node node,Vector3 parent)
    {
        Vector3 pos = node.gObj.transform.position;
        Mesh mesh = null;
        Material mat = null;
        MeshRenderer meshRenderer = null;
        try
        {
            mesh = node.gObj.GetComponent<MeshFilter>().mesh;
            meshRenderer = node.gObj.GetComponent<MeshRenderer>();
            mat = meshRenderer.material;
        }
        catch (Exception) { }
        if (mat != null) 
        {
            stringBuilder.Append("appearance Appearance {\r\n");
            stringBuilder.Append("material Material {\r\n");
            stringBuilder.Append(string.Format("diffuseColor {0} {1} {2}\r\n", mat.color.r, mat.color.g, mat.color.b));
            stringBuilder.Append("}\r\n");  //material
            stringBuilder.Append("}\r\n");   //appearance
        }
        if (mesh != null)
        {
            stringBuilder.Append("geometry DEF " + node.gObj.transform.name + "-FACES IndexedFaceSet {\r\n");
            stringBuilder.Append("ccw TRUE\r\nsolid TRUE\r\n");
            stringBuilder.Append("coord DEF " + node.gObj.transform.name + "-COORD Coordinate { point [\r\n");
            Vector3[] vertices = mesh.vertices;
            int last = 0;
            for (int i = 0; i < vertices.Length - 1; i++)
            {
                last = i + 1;
                stringBuilder.Append(string.Format("{0} {1} {2},",vertices[i].x, vertices[i].y, vertices[i].z));
            }
            if (last == vertices.Length - 1)
                stringBuilder.Append(string.Format("{0} {1} {2}\r\n]\r\n", vertices[last].x, vertices[last].y,vertices[last].z));
            stringBuilder.Append("}\r\n");   //coordinate
            stringBuilder.Append("coordIndex [\r\n");
            int[] triangles = mesh.triangles;
            for (int j = 0; j < triangles.Length / 3 - 1; j++)
            {
                last = j + 1;
                stringBuilder.Append(string.Format("{0}, {1}, {2}, -1, ", triangles[j * 3], triangles[j * 3 + 1], triangles[j * 3 + 2]));
            }
            if (last == triangles.Length / 3 - 1)
                stringBuilder.Append(string.Format("{0}, {1}, {2}, -1\r\n]\r\n", triangles[last * 3], triangles[last * 3 + 1], triangles[last * 3 + 2]));
            stringBuilder.Append("}\r\n");  //geometry
        }
    }
}
